#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <dlfcn.h>
#include <sys/msg.h>
#include "shell.h"

void Star()
{
    printf("%s called\n", __FUNCTION__);
}

void *ShellServer(void *arg)
{
    int msgId;
    ShellCmd cmd;
    void (*func)() = NULL;
    void *globalSymTbl = NULL;
    char *err = NULL;

    printf("Welcome to shell server\n");

    msgId = msgget((key_t)MSG_QUEUE_KEY, 0666 | IPC_CREAT);
    if (msgId == -1) {
        printf("Create msg queue failed\n");
        goto _exit;
    }

    // 打开全局符号表(第1个参数为0)
    globalSymTbl = dlopen(NULL, RTLD_NOW | RTLD_GLOBAL);
    if (globalSymTbl == NULL) {
        printf("Open global symbol table failed\n");
        goto _exit;
    }

    while (1) {
        if (msgrcv(msgId, (void *)&cmd, BUFSIZ, 0, 0) == -1) {
            printf("Recv from msg queue failed\n");
            break;
        }

        // printf("Recv msg: %s(%d)\n", cmd.cmd, strlen(cmd.cmd));

        cmd.cmd[strlen(cmd.cmd) - 1] = '\0'; // 去掉尾部的换行符
        if (strncmp(cmd.cmd, "kill", 4) == 0) {
            goto _exit;
        }

        // 在全局动态符号表中查找shell client发过来的字符串对应的函数, 并执行
        func = dlsym(globalSymTbl, cmd.cmd);
        if ((err = dlerror()) != NULL) {
            printf("Symbol \"%s\" not found\n", cmd.cmd);
            continue;
        }

        // 注意: dlsym查出来的符号可能不是函数(变量或其他符号), 这里没有做判断, 存在bug
        func();
    }

_exit:
    if (msgId != -1) {
        msgctl(msgId, IPC_RMID, 0);
    }
    if (globalSymTbl != NULL) {
        dlclose(globalSymTbl);
    }
    pthread_exit(0);
}

int main(int argc, char *argv[])
{
    void *handle1 = NULL;
    void *handle2 = NULL;
    pthread_t tid = 0;
    int ret;

    // 打开共享对象, 将其.dynsym中的符号合并到全局符号表中, 以供后续调用
    handle1 = dlopen("shell_lib1.so", RTLD_NOW | RTLD_GLOBAL);
    handle2 = dlopen("shell_lib2.so", RTLD_NOW | RTLD_GLOBAL);

    ret = pthread_create(&tid, NULL, ShellServer, NULL);
    if (ret != 0) {
        printf("Create shell server thread faild\n");
    }
    pthread_join(tid, NULL);

    dlclose(handle2);
    dlclose(handle1);
    return 0;
}
